package com.hsu.usercenter.easyexcel;

import com.hsu.usercenter.mapper.UserMapper;
import com.hsu.usercenter.model.domain.User;
import com.hsu.usercenter.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 批量插入用户数据的组件。
 * 使用了Spring的StopWatch工具类来监测执行时间，便于了解批处理的性能。
 * @ClassName: InsertUser
 * @author Hsu琛君珩
 * @date 2024-05-15
 * @apiNote 提供批量插入用户的方法，主要用于大量数据处理测试或初始化。
 * @Version: v1.0
 */
@Component
public class InsertUser {
    @Resource
    private UserMapper userMapper;

    @Resource
    private UserService userService;

    // 线程池的设置
    private ExecutorService executorService = new ThreadPoolExecutor(
            16, // corePoolSize: 核心线程数。线程池中始终保持活跃的线程数量，即使它们处于空闲状态。
            1000, // maximumPoolSize: 最大线程数。线程池中允许的最大线程数量。
            10000, // keepAliveTime: 当线程数超过核心线程数时，这是非核心线程空闲前的最大存活时间。
            TimeUnit.MINUTES, // 时间单位。上面的 keepAliveTime 的单位。
            new ArrayBlockingQueue<>(10000) // 工作队列。存放待执行任务的阻塞队列，具有先进先出等特性的阵列支持的有界队列。
    );

    /**
     * 执行用户数据的批量插入操作。
     * 创建了5000个用户实例，并逐一插入数据库中。
     */
    public void doInsertUser() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start(); // 开始计时
        final int INSERT_NUM = 5000; // 定义插入的用户数量
        for (int i = 0; i < INSERT_NUM; i++) {
            User user = new User();
            // 配置用户信息
            user.setUserName("假Hsu");
            user.setUserAccount("fakeHsu");
            user.setAvatarUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/PiajxSqBRaELkfM4IsxxWrB70flGuaDcq55mDxh8r4DuwOJLuluSmRCH9Pk1MFibry5icVgHtfwMmnYGqT49svVKV3X1wMer2OCC3ob5leZX5lF8HMbPo1Qww/132");
            user.setProfile("你好啊");
            user.setGender(0);
            user.setUserPassword("12345678");
            user.setPhone("123456789101");
            user.setEmail("Hsu@qq.com");
            user.setUserStatus(0);
            user.setUserRole(0);
            user.setPlanetCode("24525");
            user.setTags("[]");
            userMapper.insert(user); // 插入用户
        }
        stopWatch.stop(); // 停止计时
        // 输出插入操作所需的时间
        System.out.println("批量插入执行时间（毫秒）：" + stopWatch.getLastTaskTimeMillis());
    }

    /**
     * 执行用户数据的批量插入操作，使用批处理以提高性能。
     * 创建了5000个用户实例并将它们添加到列表中，最后通过批处理方法插入数据库。
     * 这种方法减少了数据库交互次数，从而显著提高了性能。
     */
    public void doBatchInsertUser() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start(); // 开始计时
        final int INSERT_NUM = 5000; // 定义插入的用户数量
        List<User> userList=new ArrayList<>();
        for (int i = 0; i < INSERT_NUM; i++) {
            User user = new User();
            // 配置用户信息
            user.setUserName("假Hsu");
            user.setUserAccount("fakeHsu");
            user.setAvatarUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/PiajxSqBRaELkfM4IsxxWrB70flGuaDcq55mDxh8r4DuwOJLuluSmRCH9Pk1MFibry5icVgHtfwMmnYGqT49svVKV3X1wMer2OCC3ob5leZX5lF8HMbPo1Qww/132");
            user.setProfile("你好啊");
            user.setGender(0);
            user.setUserPassword("12345678");
            user.setPhone("123456789101");
            user.setEmail("Hsu@qq.com");
            user.setUserStatus(0);
            user.setUserRole(0);
            user.setPlanetCode("24525");
            user.setTags("[]");
            userList.add(user);
        }
        userService.saveBatch(userList,100);// 使用批处理每次提交100个用户
        stopWatch.stop(); // 停止计时
        // 输出插入操作所需的时间
        System.out.println("批量插入执行时间（毫秒）：" + stopWatch.getLastTaskTimeMillis());
    }

    /**
     * 使用并发方法批量插入用户数据。
     * 通过分割任务并利用CompletableFuture与线程池，实现高效的并行数据插入。
     * 此方法尤其适用于处理大量数据插入操作，能显著提高性能。
     */
    public void doConcurrencyInsertUser() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start(); // 开始计时
        final int INSERT_NUM = 100000; // 总插入数据量
        final int batchSize = 5000; // 每批次处理的数据量
        List<CompletableFuture<Void>> futureList = new ArrayList<>();
        // 根据批次大小分割任务
        for (int i = 0; i < Math.ceil((double) INSERT_NUM / batchSize); i++) {
            List<User> userList = new ArrayList<>();
            // 创建每批次的用户数据
            for (int j = 0; j < batchSize; j++) {
                User user = new User();
                user.setUserName("假Hsu");
                user.setUserAccount("fakeHsu");
                user.setAvatarUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/PiajxSqBRaELkfM4IsxxWrB70flGuaDcq55mDxh8r4DuwOJLuluSmRCH9Pk1MFibry5icVgHtfwMmnYGqT49svVKV3X1wMer2OCC3ob5leZX5lF8HMbPo1Qww/132");
                user.setProfile("你好啊");
                user.setGender(0);
                user.setUserPassword("12345678");
                user.setPhone("123456789101");
                user.setEmail("Hsu@qq.com");
                user.setUserStatus(0);
                user.setUserRole(0);
                user.setPlanetCode("24525");
                user.setTags("[]");
                userList.add(user);
            }
            // 异步执行数据库插入操作
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                System.out.println("ThreadName：" + Thread.currentThread().getName());
                userService.saveBatch(userList, batchSize);
            }, executorService);
            futureList.add(future);
        }
        // 等待所有异步任务完成
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
        stopWatch.stop(); // 停止计时
        System.out.println("并发批量插入执行时间（毫秒）：" + stopWatch.getLastTaskTimeMillis());
    }

}
